#include "../sleipner_config.h"
#include "ogl_graphics.h"

#include "ogl_font.h"
#include "ogl_image_cache.h"
#include "ogl_buffer.h"

#include "ogl_surface.h"
#include "ogl_dynamic_image.h"

#include "util/mmgr.h"	
#include "util/common_macros.h"		// macros
#include "application/game_application.h"
#include "util/timer.h"				// log
#include "util/log.h"				// log


extern "C"
{
#ifdef _SDL2
    
    #include "SDL2/include/SDL.h"
    #include "SDL2/src/video/SDL_sysvideo.h"
    #ifdef _PLATFORM_WIN32
        #include "glee/glee.h"
        //#include "SDL-1.2.13/include/SDL_opengl.h"
    #else
#ifdef _OPENGLES2
        #include "SDL2/include/SDL_opengles2.h"
#else

        #include "SDL2/include/SDL_opengles.h"
#endif
    #endif
   
#else
    
    
    #include "SDL-1.2.13/include/SDL.h"
    #ifndef _PLATFORM_IOS
        #include "glee/glee.h"
        //#include "SDL-1.2.13/include/SDL_opengl.h"
    #else
        #include "SDL-1.2.13/include/SDL_opengles.h"
    #endif

#endif
    
}

#include <cassert>

#ifdef WIN32
#include "gameswf/gameswf.h"
gameswf::render_handler*  create_render_handler_ogl(void);
#endif

extern GameApp*	g_pApp;

#ifdef _OPENGLES2

SDL_Window *g_Window = NULL;
SDL_GLContext g_Context = NULL;

const int INDEX_VERTEX = 0;
const int INDEX_TEXTURE = 1;
const int INDEX_COLOR = 2;


unsigned int	OglGraphics::m_EGLProgram = 0;

bool	OglGraphics::InitWindow(unsigned int Width, unsigned int Height, bool Fullscreen, bool EnableDebug)
{
    
    
    m_IsWindowed					 = !Fullscreen;
	m_IsWindowed					 = true;
	
	Uint32 Flag = SDL_WINDOW_OPENGL;
        
#if defined( _PLATFORM_IOS) || defined( _PLATFORM_ANDROID)
#else
	if(!m_IsWindowed)
#endif
    {
		Flag = Flag |SDL_WINDOW_FULLSCREEN;
        
#if defined( _PLATFORM_IOS) || defined( _PLATFORM_ANDROID) 
		Flag = Flag | SDL_WINDOW_BORDERLESS;            
#endif
        
    }
    
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    
#if  defined(_PLATFORM_IOS) || defined(_PLATFORM_ANDROID)     
    g_Window = SDL_CreateWindow( NULL, 0, 0, 0, 0,  Flag);    
#else    
    g_Window = SDL_CreateWindow( NULL, 0, 0, Width, Height, Flag);    
#endif
    
	g_Context  = SDL_GL_CreateContext(g_Window);
    
    
    GLuint VertexShader;
    GLint compiled;
    // Create the shader object
    VertexShader = glCreateShader(GL_VERTEX_SHADER);
    
    if(VertexShader == 0)
        _LOG(MSG_FATAL,"can't create shader!");
    
    static const Uint8 VertexShaderSrc[] = " \
    uniform mat4 u_projection; \n\
    attribute vec2 a_position; \n\
    attribute vec2 a_texCoord; \n\
    attribute vec4 a_color; \n\
    varying vec2 v_texCoord; \n\
    varying vec4 v_color; \n\
    \n\
    void main() \n\
    { \n\
    vec2 position = (a_position); \n\
    v_texCoord = a_texCoord; \n\
    v_color = a_color / 255.0; \n\
    gl_Position = u_projection * vec4(position, 0.0, 1.0);\n\
    gl_PointSize = 1.0; \n\
    } \n\
    ";
    
    const void *data = VertexShaderSrc;
    
    // Load the shader source
    glShaderSource(VertexShader, 1, (const char **)&data, NULL);
    // Compile the shader
    glCompileShader(VertexShader);
    // Check the compile status
    glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &compiled);
 
    if(!compiled)
    {
        GLint infoLen = 0;
        glGetShaderiv(VertexShader, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 1)
        {
            char infoLog[512];
            glGetShaderInfoLog(VertexShader, infoLen, NULL, infoLog);
            std::string Temp = infoLog;
            _LOG(MSG_FATAL,Temp);
           
        }
        glDeleteShader(VertexShader);       
    }
    
    
    
    //----------
    
    GLuint FragmentShader;
   
    FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    
    if(FragmentShader == 0)
        _LOG(MSG_FATAL,"can't create fragment shader!");
    
    static const Uint8 FragmentShaderSrc[] = " \
    precision mediump float; \n\
    uniform sampler2D u_texture; \n\
    varying vec2 v_texCoord; \n\
    varying vec4 v_color; \n\
    \n\
    void main() \n\
    { \n\
    gl_FragColor = texture2D(u_texture, v_texCoord); \n\
    gl_FragColor *= v_color; \n\
    } \n\
    ";
    
    data = FragmentShaderSrc;
    
    // Load the shader source
    glShaderSource(FragmentShader, 1, (const char **)&data, NULL);
    // Compile the shader
    glCompileShader(FragmentShader);
    // Check the compile status
    glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &compiled);
    
    if(!compiled)
    {
        GLint infoLen = 0;
        glGetShaderiv(FragmentShader, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 1)
        {
            char infoLog[512];
            glGetShaderInfoLog(FragmentShader, infoLen, NULL, infoLog);
            std::string Temp = infoLog;
            _LOG(MSG_FATAL,Temp);
            
        }
        glDeleteShader(FragmentShader);
    }

    
    
    //----------
    
    GLuint ProgramObject = glCreateProgram ( );
	m_EGLProgram = ProgramObject;
    
    if (ProgramObject == 0)
        _LOG(MSG_FATAL,"can't create shader program!");
    
    glAttachShader (ProgramObject, VertexShader);
    glAttachShader (ProgramObject, FragmentShader);
    
    glLinkProgram (ProgramObject);
    GLint IsLinked;
    
    // Check the link status
    glGetProgramiv ( ProgramObject, GL_LINK_STATUS, &IsLinked );
    
    if ( !IsLinked )
    {
        GLint infoLen = 0;
        
        glGetProgramiv ( ProgramObject, GL_INFO_LOG_LENGTH, &infoLen );
        
        if ( infoLen > 1 )
        {
            char infoLog[512];
           
            glGetProgramInfoLog ( ProgramObject, infoLen, NULL, infoLog );
            std::string Temp = infoLog;
            _LOG(MSG_FATAL, Temp);
            free ( infoLog );
        }
        
        glDeleteProgram ( ProgramObject );
       
    }
    else
    {
         glUseProgram (ProgramObject);
        _LOG(MSG_INIT, "OPEN GL ES 2.0 Program Created!");
    }
    
    glBindAttribLocation(ProgramObject, INDEX_VERTEX, "a_position");
    glBindAttribLocation(ProgramObject, INDEX_TEXTURE, "a_texCoord");
    glBindAttribLocation(ProgramObject, INDEX_COLOR, "a_color");

   
    GLint LOC_TEXTURE = glGetUniformLocation (ProgramObject, "u_texture");
    GLint LOC_PROJECTION = glGetUniformLocation (ProgramObject, "u_projection");

    Width = g_Window->w;
    Height = g_Window->h;
              
     GLfloat projection[4][4];
    /* Prepare an orthographic projection */
    projection[0][0] = 2.0f / (float)Width;
    projection[0][1] = 0.0f;
    projection[0][2] = 0.0f;
    projection[0][3] = 0.0f;
    projection[1][0] = 0.0f;
    projection[1][1] = -2.0f / (float)Height;
    projection[1][2] = 0.0f;
    projection[1][3] = 0.0f;
    projection[2][0] = 0.0f;
    projection[2][1] = 0.0f;
    projection[2][2] = 0.0f;
    projection[2][3] = 0.0f;
    projection[3][0] = -1.0f;
    projection[3][1] = 1.0f;
    projection[3][2] = 0.0f;
    projection[3][3] = 1.0f;
    
    math::Viewport Vp;
	Vp.Width = Width;
	Vp.Height = Height;
    SetViewport(Vp);
    // Set the projection matrix       
    glUniformMatrix4fv(LOC_PROJECTION, 1, GL_FALSE, (GLfloat *)projection);   
   
    SDL_DisplayMode mode;
    SDL_GetCurrentDisplayMode(0, &mode);       	    
    
    m_ScreenWidth  = Width;
	m_ScreenHeight = Height;    
    
    
//#ifndef _PLATFORM_IOS       // ios use lauch image already, we dun need black screen at the very start
    glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f );
    glClear ( GL_COLOR_BUFFER_BIT );
	SDL_GL_SwapWindow(g_Window);	// be nice and show black screen
    glClearColor(0,0,0,1);	
	glClear(GL_COLOR_BUFFER_BIT );
//#endif    
    
	_LOG(MSG_COMMENT ,"VENDOR: "     << (char*)glGetString( GL_VENDOR ));
	_LOG(MSG_COMMENT ,"RENDERER: "   << (char*)glGetString( GL_RENDERER ));
	_LOG(MSG_COMMENT ,"VERSION: "    << (char*)glGetString( GL_VERSION ));
    _LOG(MSG_COMMENT ,"Width: "      <<  Width );
    _LOG(MSG_COMMENT ,"Height: "     <<  Height);
    
    GraphicsUtil::SetWindowSize( GetGraphics()->GetScreenWidth() , GetGraphics()->GetScreenHeight());
	g_pApp->GetHandler()->SetWidth(GetGraphics()->GetScreenWidth());
	g_pApp->GetHandler()->SetHeight(GetGraphics()->GetScreenHeight());
    
    GLenum  ErrCode = glGetError();
	if (ErrCode != GL_NO_ERROR)
	{
		_LOG(MSG_FATAL, "OPEN GL ES 2.0 Creation might not works!");
	}
    
	//SDL_GL_MakeCurrent(g_Window,g_Context);
	return true;
}

void	OglGraphics::EndScene()
{
	SDL_GL_SwapWindow(g_Window);	
	m_FrameID++;
}

bool	OglGraphics::Shutdown()
{
	SDL_GL_DeleteContext(g_Context);  
  
  // Done! Close the window, clean-up and exit the program. 
	SDL_DestroyWindow(g_Window);

	return true;
}



void	OglGraphics::DrawVertex2D(Vertex2D*	VertA, unsigned int VertC, PRIMITIVE_TYPE Type )
{
	while(glGetError() != GL_NO_ERROR);

    GLenum GLMode;
	unsigned int PrimitiveC = 0;
	MapPrimitiveToOGL(Type,GLMode,VertC,PrimitiveC);
    
    
    
	//glVertexPointer(2, GL_FLOAT,  sizeof(Vertex2D), VertA);
	//glTexCoordPointer(2,GL_FLOAT, sizeof(Vertex2D), &VertA->u);
	//glColorPointer(4,GL_UNSIGNED_BYTE,sizeof(Vertex2D) , &VertA->color);
    
    
	glEnableVertexAttribArray(INDEX_VERTEX);
	glEnableVertexAttribArray(INDEX_TEXTURE);
    glEnableVertexAttribArray(INDEX_COLOR);
    
    glVertexAttribPointer(INDEX_VERTEX , 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), VertA);
    glVertexAttribPointer(INDEX_TEXTURE , 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), &VertA->u);
    glVertexAttribPointer(INDEX_COLOR , 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex2D), &VertA->color);
    
	glDrawArrays(GLMode,0,VertC);
    
	glDisableVertexAttribArray(INDEX_VERTEX);
	glDisableVertexAttribArray(INDEX_TEXTURE);
    glDisableVertexAttribArray(INDEX_COLOR);    
          
	GLenum   ErrorCode = glGetError();
	if (ErrorCode != GL_NO_ERROR)
	{					
		_LOG(MSG_FATAL, "Fail To DrawVertex2D!");
	}	
    
	/*
     // debugging purpose block
     
     bool TextureEnable = glIsEnabled(GL_TEXTURE_2D);
     glDisable(GL_TEXTURE_2D);
     glBegin(GL_LINES);
     for (unsigned int i=0; i<PrimitiveC; i++)
     {
     int	BaseIndex = (i*3);
     Vertex2D*	pA = &VertA[BaseIndex+0];
     Vertex2D*	pB = &VertA[BaseIndex+1];
     Vertex2D*	pC = &VertA[BaseIndex+2];
     
     glColor4ubv((GLubyte*)&pA->color);
     
     glVertex2f(pA->x,pA->y);
     glVertex2f(pB->x,pB->y);
     
     glVertex2f(pB->x,pB->y);
     glVertex2f(pC->x,pC->y);
     
     glVertex2f(pC->x,pC->y);
     glVertex2f(pA->x,pA->y);
     
     }
     glEnd();
     if(TextureEnable)
     glEnable(GL_TEXTURE_2D);
     */
    
   
	
}

void	OglGraphics::DrawIndexVertex2D(Vertex2D*	VertA, unsigned int VertC, unsigned short* IndexA, unsigned int IndexC, PRIMITIVE_TYPE Type)
{
	while(glGetError() != GL_NO_ERROR);

	GLenum GLMode;
	unsigned int PrimitiveC = 0;
	MapPrimitiveToOGL(Type,GLMode,IndexC,PrimitiveC);
	if(GLMode == GL_TRIANGLES)
	{
		//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		//glEnableClientState(GL_COLOR_ARRAY);
		//glEnableClientState(GL_VERTEX_ARRAY);
        
		//glVertexPointer(2, GL_FLOAT,  sizeof(Vertex2D), VertA);
        
        
        glVertexAttribPointer(0 , 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), &VertA);
		//glTexCoordPointer(2,GL_FLOAT, sizeof(Vertex2D), &VertA->u);
		//glColorPointer(4,GL_UNSIGNED_BYTE,sizeof(Vertex2D) , &VertA->color);
		//glInterleavedArrays(GL_T2F_C4UB_V3F,sizeof(Vertex2D),VertA);
		
		glDrawElements(GL_TRIANGLES, IndexC , GL_UNSIGNED_SHORT, IndexA);
		//glDrawArrays(GLMode,0,VertC);
        
		//glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		//glDisableClientState(GL_COLOR_ARRAY);
		//glDisableClientState(GL_VERTEX_ARRAY);
        
		/*glDisable(GL_TEXTURE_2D);
         glBegin(GL_LINES);
         for (unsigned int i=0; i<VertC/3; i++)
         {
         int	BaseIndex = (i*3);
         Vertex2D*	pA = &VertA[BaseIndex+0];
         Vertex2D*	pB = &VertA[BaseIndex+1];
         Vertex2D*	pC = &VertA[BaseIndex+2];
         
         glColor4ubv((GLubyte*)&pA->color);
         
         glVertex2f(pA->x, pA->y);
         glVertex2f(pB->x, pB->y);
         
         glVertex2f(pB->x, pB->y);
         glVertex2f(pC->x, pC->y);
         
         glVertex2f(pC->x, pC->y);
         glVertex2f(pA->x, pA->y);
         
         }
         glEnd();
         glEnable(GL_TEXTURE_2D);*/
        
	}

	GLenum   ErrorCode = glGetError();
	if (ErrorCode != GL_NO_ERROR)
	{			
		_LOG(MSG_FATAL, "Fail To DrawIndexVertex2D!");
	}	
    
    
}



#endif   // _OPENGLES2


